﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Remoting.Channels;
using System.Reflection;
using System.Runtime.Remoting;
using System.Net;
using System.Runtime.Remoting.Channels.TwoWayTcp;

namespace SanguoSharp
{
    /// <summary>
    /// Contains commonly used methods and constants.
    /// </summary>
    public static class Utils
    {
        /// <summary>
        /// The major interface version.
        /// </summary>
        public const int InterfaceVersionMajor = 6;
        /// <summary>
        /// The minor interface version.
        /// </summary>
        public const int InterfaceVersionMinor = 0;

        /// <summary>
        /// Checks the interface version compatibility for the specified server.
        /// </summary>
        /// <param name="server">
        /// The <see cref="IServer"/> to check.
        /// </param>
        /// <returns>
        /// True if the server is compatible, otherwise false.
        /// </returns>
        public static bool IsServerCompatible(IServer server)
        {
            if (server.InterfaceVersionMajor != InterfaceVersionMajor)
                return false;
            if (server.InterfaceVersionMinor < InterfaceVersionMinor)
                return false;
            return true;
        }

        /// <summary>
        /// The path to the folder where Bang# applications should store configuration files.
        /// </summary>
        public static readonly string ConfigFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SanguoSharp");

#if CONST_SEED
		// For debugging purposes you can use constant seed if you need to reproduce certain situations.
		private const int Seed = 10;
#else
        private static readonly int Seed = Environment.TickCount;
#endif

        /// <summary>
        /// A shared instance of random number generator.
        /// </summary>
        public static readonly Random Random = new Random(Seed);

        private static readonly CardType[] MainCards = new CardType[]
		{
			CardType.杀,
			CardType.闪,
			CardType.桃,
			CardType.无中生有,
			CardType.顺手牵羊,
			CardType.桃园结义,
			CardType.五谷丰登,
			CardType.爪黄飞电,
            CardType.诸葛连弩,
            CardType.绝影,
            CardType.赤兔,
			CardType.丈八蛇矛,
			CardType.万箭齐发,
			CardType.闪电,
			CardType.青龙偃月刀,
			CardType.麒麟弓,
			CardType.南蛮入侵,
			CardType.乐不思蜀,
			CardType.决斗,
			CardType.八卦阵,
			CardType.雌雄双股剑,
			CardType.方天画戟,
			CardType.贯石斧,
			CardType.过河拆桥,
			CardType.借刀杀人,
            CardType.青釭剑,
            CardType.仁王盾,
            CardType.无懈可击,
		};
        private static CardType[] ExExCards = new CardType[]
		{
			CardType.白银狮子,
			CardType.兵粮寸断,
			CardType.大宛,
			CardType.的卢,
			CardType.古锭刀,
			CardType.寒冰剑,
			CardType.酒,
			CardType.雷杀,
			CardType.藤甲,
			CardType.铁索连环,
			CardType.紫骍,
			CardType.火杀,
			CardType.火攻,
			CardType.朱雀羽扇,
		};
        public static List<CardType> GetCardTypes()
        {
            return GetCardTypes(null, false);
        }
        public static List<CardType> GetCardTypes(bool includeDefault)
        {
            return GetCardTypes(null, includeDefault);
        }
        public static List<CardType> GetCardTypes(ISession session)
        {
            return GetCardTypes(session, false);
        }
        public static List<CardType> GetCardTypes(ISession session, bool includeDefault)
        {
            int count = MainCards.Length;
            if (includeDefault)
                count++;

            if (session != null)
            {
                if (session.ExEx)
                    count += ExExCards.Length;
                //...
            }

            List<CardType> list = new List<CardType>(count);
            list.AddRange(MainCards);
            if (includeDefault)
                list.Add(CardType.未知);

            if (session != null)
            {
                if (session.ExEx)
                    list.AddRange(ExExCards);
                //...
            }
            return list;
        }

        private static readonly Role[] Roles = new Role[]
		{
			Role.主公,
			Role.忠臣,
			Role.反贼,
			Role.内奸,
		};
        public static List<Role> GetRoles()
        {
            return GetRoles(false);
        }
        public static List<Role> GetRoles(bool includeDefault)
        {
            int count = Roles.Length;
            if (includeDefault)
                count++;

            List<Role> list = new List<Role>(count);
            list.AddRange(Roles);

            if (includeDefault)
                list.Add(Role.未知);
            return list;
        }

        private static readonly CharacterType[] MainCharacters = new CharacterType[]
		{
			CharacterType.曹操,
			CharacterType.大乔,
			CharacterType.貂蝉,
			CharacterType.甘宁,
			CharacterType.郭嘉,
			CharacterType.华佗,
			CharacterType.黄盖,
			CharacterType.黄月英,
			CharacterType.黄忠,
			CharacterType.刘备,
			CharacterType.陆逊,
			CharacterType.吕布,
			CharacterType.吕蒙,
			CharacterType.马超,
			CharacterType.司马懿,
			CharacterType.孙尚香,
			CharacterType.夏侯惇,
			CharacterType.张飞,
			CharacterType.张辽,
			CharacterType.赵云,
			CharacterType.甄姬,
			CharacterType.周瑜,
			CharacterType.诸葛亮,
		};
        private static readonly CharacterType[] ExExCharacters = new CharacterType[]
        {
        //    CharacterType.ApacheKid,
        //    CharacterType.BelleStar,
        //    CharacterType.BillNoface,
        //    CharacterType.ChuckWengam,
        //    CharacterType.DocHolyday,
        //    CharacterType.ElenaFuente,
        //    CharacterType.GregDigger,
        //    CharacterType.HerbHunter,
        //    CharacterType.JoseDelgado,
        //    CharacterType.MollyStark,
        //    CharacterType.PatBrennan,
        //    CharacterType.PixiePete,
        //    CharacterType.SeanMallory,
        //    CharacterType.TequilaJoe,
        //    CharacterType.VeraCuster,
			//CharacterType.,
		};
        public static List<CharacterType> GetCharacterTypes()
        {
            return GetCharacterTypes(null, false);
        }
        public static List<CharacterType> GetCharacterTypes(bool includeDefault)
        {
            return GetCharacterTypes(null, includeDefault);
        }
        public static List<CharacterType> GetCharacterTypes(ISession session)
        {
            return GetCharacterTypes(session, false);
        }
        public static List<CharacterType> GetCharacterTypes(ISession session, bool includeDefault)
        {
            int count = MainCharacters.Length;
            if (includeDefault)
                ++count;

            if (session != null)
            {
                if (session.ExEx)
                    count += ExExCharacters.Length;
                //...
            }

            List<CharacterType> list = new List<CharacterType>(count);
            list.AddRange(MainCharacters);
            if (includeDefault)
                list.Add(CharacterType.未知);

            if (session != null)
            {
                if (session.ExEx)
                    list.AddRange(ExExCharacters);
                //...
            }
            return list;
        }

        /// <summary>
        /// Shuffles the list.
        /// </summary>
        /// <param name="list">
        /// The <see cref="List<T>"/> to be shuffled.
        /// </param>
        public static void Shuffle<T>(this List<T> list)
        {
            // Wanna more randomity? Just increase the repeat count :-)
            for (int c = 0; c < 10; c++)
            {
                for (int i = 0; i < list.Count; i++)
                {
                    int k = Random.Next(list.Count);
                    T temp = list[i];
                    list[i] = list[k];
                    list[k] = temp;
                }
            }
        }
        /// <summary>
        /// Generates a new id for the dictionary.
        /// </summary>
        /// <param name="dict">
        /// The <see cref="Dictionary<int, T>"/> for which to generate a new id.
        /// </param>
        /// <returns>
        /// The generated id.
        /// </returns>
        public static int GenerateID<T>(this Dictionary<int, T> dict)
        {
            int id = 1;
            // id 0 is reserved (like null)
            while (dict.ContainsKey(id))
                if (++id == 0)
                    // This should never happen, but... you never know :)
                    throw new OverflowException("Out of available IDs!");
            return id;
        }

        /// <summary>
        /// Gets a random element from the specified list.
        /// </summary>
        /// <param name="list">
        /// The <see cref="IList<T>"/> from which to get a random element.
        /// </param>
        /// <returns>
        /// A random element from the list.
        /// </returns>
        public static T GetRandom<T>(this IList<T> list)
        {
            if (list.Count == 0)
                throw new InvalidOperationException();
            return list[Random.Next(list.Count)];
        }

        private static IServerChannelSinkProvider GetServerProvider(IEnumerable<Type> allowedTypes)
        {
            MethodRestrictionServerSinkProvider restrictionProvider = new MethodRestrictionServerSinkProvider();
            restrictionProvider.Filter = m =>
            {
                Type decl = m.DeclaringType;
                if (decl.Equals(typeof(object)))
                    return true;
                foreach (Type t in allowedTypes)
                    if (t.IsAssignableFrom(decl))
                        foreach (MethodInfo mi in decl.GetInterfaceMap(t).TargetMethods)
                            if (mi.MethodHandle == m.MethodHandle)
                                return true;
                return false;
            };
            BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
            serverProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
            serverProvider.Next = restrictionProvider;
            return serverProvider;
        }
        private static IClientChannelSinkProvider GetClientProvider()
        {
            return new BinaryClientFormatterSinkProvider();
        }

        public static T Connect<T>(string uri, string address, int port, IEnumerable<Type> allowedTypes)
        {
            ChannelServices.RegisterChannel(new TcpClientChannel("client", GetClientProvider(), GetServerProvider(allowedTypes)), false);
            //ChannelServices.RegisterChannel(new TcpClientChannel("client", GetClientProvider()), false);
            return (T)RemotingServices.Connect(typeof(T), "tcp://" + address + ":" + port + "/" + uri);
        }
        public static void Serve<T>(string uri, int port, IEnumerable<Type> allowedTypes, IPAddress bindTo)
            where T : MarshalByRefObject, new()
        {
            Dictionary<string, object> properties = new Dictionary<string, object>();
            properties.Add("name", "server:" + port);
            properties.Add("port", port);
            properties.Add("bindTo", bindTo.ToString());
            TcpServerChannel channel = new TcpServerChannel(properties, GetClientProvider(), GetServerProvider(allowedTypes));
            ChannelServices.RegisterChannel(channel, false);
            RemotingConfiguration.RegisterWellKnownServiceType(typeof(T), uri, WellKnownObjectMode.Singleton);
        }

        public static readonly Type[] ClientSharedTypes = new Type[]
		{
			typeof(IEventListener),
			typeof(IPlayerEventListener),
			typeof(ISpectatorEventListener),
		};
        public static readonly Type[] ServerSharedTypes = new Type[]
		{
			typeof(IServer),
			typeof(ISession),
			typeof(IPlayer),
			typeof(ISpectator),
			typeof(IPlayerSessionControl),
			typeof(ISpectatorSessionControl),
			typeof(IGame),
			typeof(IPublicPlayerView),
			typeof(IPrivatePlayerView),
			typeof(IPlayerControl),
			typeof(ISpectatorControl),
			typeof(ICard),
			typeof(IIdentificable),
		};

        /// <summary>
        /// Connects to the Bang# server with the specified address and port.
        /// </summary>
        /// <param name="address">
        /// The address of the server.
        /// </param>
        /// <param name="port">
        /// The port for the Bang# service.
        /// </param>
        /// <returns>
        /// The <see cref="Bang.IServer"/> object from the server.
        /// </returns>
        public static IServer Connect(string address, int port)
        {
            return Connect<IServer>("SanguoSharp.rem", address, port, ClientSharedTypes);
        }
        /// <summary>
        /// Starts serving the Bang# service at the specified port.
        /// </summary>
        /// <param name="port">
        /// The port on which to listen.
        /// </param>
        /// <typeparam name="T">
        /// The service type (must implement the <see cref="IServer"/> interface).
        /// </typeparam>
        public static void Serve<T>(int port)
            where T : MarshalByRefObject, IServer, new()
        {
            Serve<T>("SanguoSharp.rem", port, ServerSharedTypes, IPAddress.Any);
        }
    }
}
